/*
	The Raja Toolkit.
	Copyright (C) 2011 Raja-Toolkit Foundation

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 3.0 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/


#ifndef RAJA_MATH_VECTOR
#define RAJA_MATH_VECTOR
/*
 *
 *
 */

#include "../types.hpp"

namespace raja
{
namespace math
{
template<typename T> struct tvec2;
template<typename T> struct tvec3;
template<typename T> struct tvec4;

typedef struct tvec2<sint32> ivec2;
typedef struct tvec2<real32> vec2;
typedef struct tvec2<real64> dvec2;
typedef struct tvec2<bool> bvec2;

typedef struct tvec3<sint32> ivec3;
typedef struct tvec3<real32> vec3;
typedef struct tvec3<real64> dvec3;
typedef struct tvec3<bool> bvec3;

typedef struct tvec4<sint32> ivec4;
typedef struct tvec4<real32> vec4;
typedef struct tvec4<real64> dvec4;
typedef struct tvec4<bool> bvec4;

template<typename T>
struct tvec2
{
	typedef T	atom_type;

	union
	{
		struct
		{
			T x, y;
		};
		struct
		{
			T r, g;
		};
		struct
		{
			T s, t;
		};
	};

	/// constructor
	tvec2() :
		x(0), y(0)
	{
	}

	/// constructors/conversion
	template<typename Y> explicit tvec2(Y _f) :
		x(_f), y(_f)
	{
	}
	template<typename X, typename Y> explicit tvec2(X _x, Y _y) :
		x(_x), y(_y)
	{
	}
	template<typename Y> explicit tvec2(const tvec2<Y> &v) :
		x(v.x), y(v.y)
	{
	}
	template<typename Y> explicit tvec2(const tvec3<Y> &v) :
		x(v.x), y(v.y)
	{
	}
	template<typename Y> explicit tvec2(const tvec4<Y> &v) :
		x(v.x), y(v.y)
	{
	}

	/// access
	T& operator[](int i)
	{
		return (&x)[i];
	}

	///
	/// operators
	///

	/// addition
	template<typename Y>
	tvec2& operator +=(const tvec2<Y> &v)
	{
		x += v.x;
		y += v.y;
		return (*this);
	}

	template<typename Y>
	tvec2& operator +=(Y v)
	{
		x += v;
		y += v;
		return (*this);
	}

	/// substraction
	template<typename Y>
	tvec2& operator -=(const tvec2<Y> &v)
	{
		x -= v.x;
		y -= v.y;
		return (*this);
	}

	template<typename Y>
	tvec2& operator -=(Y v)
	{
		x -= v;
		y -= v;
		return (*this);
	}

	/// multiplication
	template<typename Y>
	tvec2& operator *=(const tvec2<Y> &v)
	{
		x *= v.x;
		y *= v.y;
		return (*this);
	}

	template<typename Y>
	tvec2& operator *=(Y v)
	{
		x *= v;
		y *= v;
		return (*this);
	}

	/// division
	template<typename Y>
	tvec2& operator /=(const tvec2<Y> &v)
	{
		x /= v.x;
		y /= v.y;
		return (*this);
	}

	template<typename Y>
	tvec2& operator /=(Y v)
	{
		x /= v;
		y /= v;
		return (*this);
	}

	/// prefix increment
	tvec2& operator++()
	{
		++x;
		++y;
		return (*this);
	}

	/// prefix decrement
	tvec2& operator--()
	{
		--x;
		--y;
		return (*this);
	}

	/// postfix increment
	tvec2 operator++(int)
	{
		tvec2 res(*this);
		++(*this);
		return res;
	}

	/// postfix decrement
	tvec2 operator--(int)
	{
		tvec2 res(*this);
		--(*this);
		return res;
	}

	/// assignment
	template<typename Y>
	tvec2& operator =(const tvec2<Y> &v)
	{
		x = T(v.x);
		y = T(v.y);
		return (*this);
	}
};

template<typename T>
struct tvec3
{
	typedef T	atom_type;

	union
	{
		struct
		{
			T x, y, z;
		};
		struct
		{
			T r, g, b;
		};
		struct
		{
			T s, t, p;
		};
	};

	/// constructor
	tvec3() :
		x(0), y(0), z(0)
	{
	}

	/// constructors/conversion
	template<typename Y> explicit tvec3(Y _f) :
		x(_f), y(_f), z(_f)
	{
	}
	template<typename X, typename Y> explicit tvec3(X x_, Y y_) :
		x(x_), y(y_), z(0)
	{
	}
	template<typename X, typename Y, typename Z> explicit tvec3(X x_, Y y_, Z z_) :
		x(x_), y(y_), z(z_)
	{
	}
	template<typename XY, typename Z> explicit tvec3(const tvec2<XY> &v, Z z_ = Z(0)) :
		x(v.x), y(v.y), z(z_)
	{
	}
	template<typename Y> explicit tvec3(const tvec3<Y> &v) :
		x(v.x), y(v.y), z(v.z)
	{
	}
	template<typename Y> explicit tvec3(const tvec4<Y> &v) :
		x(v.x), y(v.y), z(v.z)
	{
	}

	/// access
	T& operator[](int i)
	{
		return (&x)[i];
	}

	///
	/// operators
	///

	/// addition
	template<typename Y>
	tvec3& operator +=(const tvec3<Y> &v)
	{
		x += v.x;
		y += v.y;
		z += v.z;
		return (*this);
	}

	template<typename Y>
	tvec3& operator +=(Y v)
	{
		x += v;
		y += v;
		z += v;
		return (*this);
	}

	/// substraction
	template<typename Y>
	tvec3& operator -=(const tvec3<Y> &v)
	{
		x -= v.x;
		y -= v.y;
		z -= v.z;
		return (*this);
	}

	template<typename Y>
	tvec3& operator -=(Y v)
	{
		x -= v;
		y -= v;
		z -= v;
		return (*this);
	}

	/// multiplication
	template<typename Y>
	tvec3& operator *=(const tvec3<Y> &v)
	{
		x *= v.x;
		y *= v.y;
		z *= v.z;
		return (*this);
	}

	template<typename Y>
	tvec3& operator *=(Y v)
	{
		x *= v;
		y *= v;
		z *= v;
		return (*this);
	}

	/// division
	template<typename Y>
	tvec3& operator /=(const tvec3<Y> &v)
	{
		x /= v.x;
		y /= v.y;
		z /= v.z;
		return (*this);
	}

	template<typename Y>
	tvec3& operator /=(Y v)
	{
		x /= v;
		y /= v;
		z /= v;
		return (*this);
	}

	/// prefix increment
	tvec3& operator++()
	{
		++x;
		++y;
		++z;
		return (*this);
	}

	/// prefix decrement
	tvec3& operator--()
	{
		--x;
		--y;
		--z;
		return (*this);
	}

	/// postfix increment
	tvec3 operator++(int)
	{
		tvec3 res(*this);
		++(*this);
		return res;
	}

	/// postfix decrement
	tvec3 operator--(int)
	{
		tvec3 res(*this);
		--(*this);
		return res;
	}

	/// assignment
	template<typename Y>
	tvec3& operator =(const tvec3<Y> &v)
	{
		x = T(v.x);
		y = T(v.y);
		z = T(v.z);
		return (*this);
	}
};

template<typename T>
struct tvec4
{
	typedef T	atom_type;

	union
	{
		struct
		{
			T x, y, z, w;
		};
		struct
		{
			T r, g, b, a;
		};
		struct
		{
			T s, t, p, q;
		};
	};

	/// constructor
	tvec4() :
		x(0), y(0), z(0), w(0)
	{
	}

	/// constructors/conversion
	template<typename Y> explicit tvec4(Y f_) :
		x(f_), y(f_), z(f_), w(f_)
	{
	}
	template<typename X, typename Y> explicit tvec4(X x_, Y y_) :
		x(x_), y(y_), z(0), w(0)
	{
	}
	template<typename X, typename Y, typename Z> explicit tvec4(X x_, Y y_, Z z_) :
		x(x_), y(y_), z(z_), w(0)
	{
	}
	template<typename X, typename Y, typename Z, typename W> explicit tvec4(X x_,
																			Y y_,
																			Z z_,
																			W w_) :
		x(x_), y(y_), z(z_), w(w_)
	{
	}
	template<typename XY> explicit tvec4(const tvec2<XY> &v) :
		x(v.x), y(v.y), z(0), w(0)
	{
	}
	template<typename XY, typename Z> explicit tvec4(const tvec2<XY> &v, Z z_) :
		x(v.x), y(v.y), z(z_), w(0)
	{
	}
	template<typename XY, typename Z, typename W> explicit tvec4(const tvec2<XY> &v, Z z_, W w_) :
		x(v.x), y(v.y), z(z_), w(w_)
	{
	}
	template<typename XYZ, typename W> explicit tvec4(const tvec3<XYZ> &v, W w_) :
		x(v.x), y(v.y), z(v.z), w(w_)
	{
	}
	template<typename Y> explicit tvec4(const tvec4<Y> &v) :
		x(v.x), y(v.y), z(v.z), w(v.w)
	{
	}

	/// access
	T& operator[](int i)
	{
		return (&x)[i];
	}

	///
	/// operators
	///

	/// addition
	template<typename Y>
	tvec4& operator +=(const tvec4<Y> &v)
	{
		x += v.x;
		y += v.y;
		z += v.z;
		w += v.w;
		return (*this);
	}

	template<typename Y>
	tvec4& operator +=(Y v)
	{
		x += v;
		y += v;
		z += v;
		w += v;
		return (*this);
	}

	/// substraction
	template<typename Y>
	tvec4& operator -=(const tvec4<Y> &v)
	{
		x -= v.x;
		y -= v.y;
		z -= v.z;
		w -= v.w;
		return (*this);
	}

	template<typename Y>
	tvec4& operator -=(Y v)
	{
		x -= v;
		y -= v;
		z -= v;
		w -= v;
		return (*this);
	}

	/// multiplication
	template<typename Y>
	tvec4& operator *=(const tvec4<Y> &v)
	{
		x *= v.x;
		y *= v.y;
		z *= v.z;
		w *= v.w;
		return (*this);
	}

	template<typename Y>
	tvec4& operator *=(Y v)
	{
		x *= v;
		y *= v;
		z *= v;
		w *= v;
		return (*this);
	}

	/// division
	template<typename Y>
	tvec4& operator /=(const tvec4<Y> &v)
	{
		x /= v.x;
		y /= v.y;
		z /= v.z;
		w /= v.w;
		return (*this);
	}

	template<typename Y>
	tvec4& operator /=(Y v)
	{
		x /= v;
		y /= v;
		z /= v;
		w /= v;
		return (*this);
	}

	/// prefix increment
	tvec4& operator++()
	{
		++x;
		++y;
		++z;
		++w;
		return (*this);
	}

	/// prefix decrement
	tvec4& operator--()
	{
		--x;
		--y;
		--z;
		--w;
		return (*this);
	}

	/// postfix increment
	tvec4 operator++(int)
	{
		tvec4 res(*this);
		++(*this);
		return res;
	}

	/// postfix decrement
	tvec4 operator--(int)
	{
		tvec4 res(*this);
		--(*this);
		return res;
	}

	/// assignment
	template<typename Y>
	tvec4& operator =(const tvec4<Y> &v)
	{
		x = T(v.x);
		y = T(v.y);
		z = T(v.z);
		w = T(v.w);
		return (*this);
	}
};

///
/// vector operator
///

/// unary +
template<typename T> tvec2<T> operator +(const tvec2<T> &v)
{
	return v;
}
template<typename T> tvec3<T> operator +(const tvec3<T> &v)
{
	return v;
}
template<typename T> tvec4<T> operator +(const tvec4<T> &v)
{
	return v;
}

/// unary -
template<typename T> tvec2<T> operator -(const tvec2<T> &v)
{
	return tvec2<T> (-v.x, -v.y);
}
template<typename T> tvec3<T> operator -(const tvec3<T> &v)
{
	return tvec3<T> (-v.x, -v.y, -v.z);
}
template<typename T> tvec4<T> operator -(const tvec4<T> &v)
{
	return tvec4<T> (-v.x, -v.y, -v.z, -v.w);
}

/// addition

template<typename T> tvec2<T> operator +(const tvec2<T> &v0,
										 const tvec2<T> &v1)
{
	return tvec2<T> (v0.x + v1.x, v0.y + v1.y);
}

template<typename T> tvec3<T> operator +(const tvec3<T> &v0,
										 const tvec3<T> &v1)
{
	return tvec3<T> (v0.x + v1.x, v0.y + v1.y, v0.z + v1.z);
}

template<typename T> tvec4<T> operator +(const tvec4<T> &v0,
										 const tvec4<T> &v1)
{
	return tvec4<T> (v0.x + v1.x, v0.y + v1.y, v0.z + v1.z, v0.w + v1.w);
}

template<typename T> tvec2<T> operator +(const tvec2<T> &v0, T v1)
{
	return tvec2<T> (v0.x + v1, v0.y + v1);
}

template<typename T> tvec3<T> operator +(const tvec3<T> &v0, T v1)
{
	return tvec3<T> (v0.x + v1, v0.y + v1, v0.z + v1);
}

template<typename T> tvec4<T> operator +(const tvec4<T> &v0, T v1)
{
	return tvec4<T> (v0.x + v1, v0.y + v1, v0.z + v1, v0.w + v1);
}

template<typename T> tvec2<T> operator +(T v0, const tvec2<T> &v1)
{
	return tvec2<T> (v0 + v1.x, v0 + v1.y);
}

template<typename T> tvec3<T> operator +(T v0, const tvec3<T> &v1)
{
	return tvec3<T> (v0 + v1.x, v0 + v1.y, v0 + v1.z);
}

template<typename T> tvec4<T> operator +(T v0, const tvec4<T> &v1)
{
	return tvec4<T> (v0 + v1.x, v0 + v1.y, v0 + v1.z, v0 + v1.w);
}

/// substraction

template<typename T> tvec2<T> operator -(const tvec2<T> &v0,
										 const tvec2<T> &v1)
{
	return tvec2<T> (v0.x - v1.x, v0.y - v1.y);
}

template<typename T> tvec3<T> operator -(const tvec3<T> &v0,
										 const tvec3<T> &v1)
{
	return tvec3<T> (v0.x - v1.x, v0.y - v1.y, v0.z - v1.z);
}

template<typename T> tvec4<T> operator -(const tvec4<T> &v0,
										 const tvec4<T> &v1)
{
	return tvec4<T> (v0.x - v1.x, v0.y - v1.y, v0.z - v1.z, v0.w - v1.w);
}

template<typename T> tvec2<T> operator -(const tvec2<T> &v0, T v1)
{
	return tvec2<T> (v0.x - v1, v0.y - v1);
}

template<typename T> tvec3<T> operator -(const tvec3<T> &v0, T v1)
{
	return tvec3<T> (v0.x - v1, v0.y - v1, v0.z - v1);
}

template<typename T> tvec4<T> operator -(const tvec4<T> &v0, T v1)
{
	return tvec4<T> (v0.x - v1, v0.y - v1, v0.z - v1, v0.w - v1);
}

template<typename T> tvec2<T> operator -(T v0, const tvec2<T> &v1)
{
	return tvec2<T> (v0 - v1.x, v0 - v1.y);
}

template<typename T> tvec3<T> operator -(T v0, const tvec3<T> &v1)
{
	return tvec3<T> (v0 - v1.x, v0 - v1.y, v0 - v1.z);
}

template<typename T> tvec4<T> operator -(T v0, const tvec4<T> &v1)
{
	return tvec4<T> (v0 - v1.x, v0 - v1.y, v0 - v1.z, v0 - v1.w);
}
/// multiplication

template<typename T> tvec2<T> operator *(const tvec2<T> &v0,
										 const tvec2<T> &v1)
{
	return tvec2<T> (v0.x * v1.x, v0.y * v1.y);
}

template<typename T> tvec3<T> operator *(const tvec3<T> &v0,
										 const tvec3<T> &v1)
{
	return tvec3<T> (v0.x * v1.x, v0.y * v1.y, v0.z * v1.z);
}

template<typename T> tvec4<T> operator *(const tvec4<T> &v0,
										 const tvec4<T> &v1)
{
	return tvec4<T> (v0.x * v1.x, v0.y * v1.y, v0.z * v1.z, v0.w * v1.w);
}

template<typename T> tvec2<T> operator *(const tvec2<T> &v0, T v1)
{
	return tvec2<T> (v0.x * v1, v0.y * v1);
}

template<typename T> tvec3<T> operator *(const tvec3<T> &v0, T v1)
{
	return tvec3<T> (v0.x * v1, v0.y * v1, v0.z * v1);
}

template<typename T> tvec4<T> operator *(const tvec4<T> &v0, T v1)
{
	return tvec4<T> (v0.x * v1, v0.y * v1, v0.z * v1, v0.w * v1);
}

template<typename T> tvec2<T> operator *(T v0, const tvec2<T> &v1)
{
	return tvec2<T> (v0 * v1.x, v0 * v1.y);
}

template<typename T> tvec3<T> operator *(T v0, const tvec3<T> &v1)
{
	return tvec3<T> (v0 * v1.x, v0 * v1.y, v0 * v1.z);
}

template<typename T> tvec4<T> operator *(T v0, const tvec4<T> &v1)
{
	return tvec4<T> (v0 * v1.x, v0 * v1.y, v0 * v1.z, v0 * v1.w);
}
/// division

template<typename T> tvec2<T> operator /(const tvec2<T> &v0,
										 const tvec2<T> &v1)
{
	return tvec2<T> (v0.x / v1.x, v0.y / v1.y);
}

template<typename T> tvec3<T> operator /(const tvec3<T> &v0,
										 const tvec3<T> &v1)
{
	return tvec3<T> (v0.x / v1.x, v0.y / v1.y, v0.z / v1.z);
}

template<typename T> tvec4<T> operator /(const tvec4<T> &v0,
										 const tvec4<T> &v1)
{
	return tvec4<T> (v0.x / v1.x, v0.y / v1.y, v0.z / v1.z, v0.w / v1.w);
}

template<typename T> tvec2<T> operator /(const tvec2<T> &v0, T v1)
{
	return tvec2<T> (v0.x / v1, v0.y / v1);
}

template<typename T> tvec3<T> operator /(const tvec3<T> &v0, T v1)
{
	return tvec3<T> (v0.x / v1, v0.y / v1, v0.z / v1);
}

template<typename T> tvec4<T> operator /(const tvec4<T> &v0, T v1)
{
	return tvec4<T> (v0.x / v1, v0.y / v1, v0.z / v1, v0.w / v1);
}

template<typename T> tvec2<T> operator /(T v0, const tvec2<T> &v1)
{
	return tvec2<T> (v0 / v1.x, v0 / v1.y);
}

template<typename T> tvec3<T> operator /(T v0, const tvec3<T> &v1)
{
	return tvec3<T> (v0 / v1.x, v0 / v1.y, v0 / v1.z);
}

template<typename T> tvec4<T> operator /(T v0, const tvec4<T> &v1)
{
	return tvec4<T> (v0 / v1.x, v0 / v1.y, v0 / v1.z, v0 / v1.w);
}

/// comparison
template<typename T> bool operator ==(const tvec2<T> &v0,
									  const tvec2<T> &v1)
{
	return (v0.x == v1.x && v0.y == v1.y);
}

template<typename T> bool operator ==(const tvec3<T> &v0,
									  const tvec3<T> &v1)
{
	return (v0.x == v1.x && v0.y == v1.y && v0.z == v1.z);
}

template<typename T> bool operator ==(const tvec4<T> &v0,
									  const tvec4<T> &v1)
{
	return (v0.x == v1.x && v0.y == v1.y && v0.z == v1.z && v0.w
			== v1.w);
}

template<typename T> bool operator !=(const tvec2<T> &v0,
									  const tvec2<T> &v1)
{
	return !(v0 == v1);
}

template<typename T> bool operator !=(const tvec3<T> &v0,
									  const tvec3<T> &v1)
{
	return !(v0 == v1);
}

template<typename T> bool operator !=(const tvec4<T> &v0,
									  const tvec4<T> &v1)
{
	return !(v0 == v1);
}

///
/// geometric operation
///

/// length
template<typename T> real32 length(const tvec2<T> &v)
{
	return std::sqrt(v.x * v.x + v.y * v.y);
}

template<typename T> real32 length(const tvec3<T> &v)
{
	return std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}

template<typename T> real32 length(const tvec4<T> &v)
{
	return std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w);
}

/// distance
template<typename T> real32 distance(const tvec2<T> &v0,
									 const tvec2<T> &v1)
{
	return length(v1 - v0);
}

template<typename T> real32 distance(const tvec3<T> &v0,
									 const tvec3<T> &v1)
{
	return length(v1 - v0);
}

template<typename T> real32 distance(const tvec4<T> &v0,
									 const tvec4<T> &v1)
{
	return length(v1 - v0);
}

/// dot product
template<typename T> T dot(const tvec2<T> &v0, const tvec2<T> &v1)
{
	return (v0.x * v1.x + v0.y * v1.y);
}

template<typename T> T dot(const tvec3<T> &v0, const tvec3<T> &v1)
{
	return (v0.x * v1.x + v0.y * v1.y + v0.z * v1.z);
}

template<typename T> T dot(const tvec4<T> &v0, const tvec4<T> &v1)
{
	return (v0.x * v1.x + v0.y * v1.y + v0.z * v1.z + v0.w * v1.w);
}

/// cross product
template<typename T> tvec3<T> cross(const tvec3<T> &v0,
									const tvec3<T> &v1)
{
	return tvec3<T> (v0.y * v1.z - v0.z * v1.y,
					 v0.z * v1.x - v0.x * v1.z,
					 v0.x * v1.y - v0.y * v1.x);
}

/// normalize
template<typename T> tvec2<T> normalize(const tvec2<T> &v)
{
	T len = length(v);
	return tvec2<T> (v.x / len, v.y / len);
}

template<typename T> tvec3<T> normalize(const tvec3<T> &v)
{
	T len = length(v);
	return tvec3<T> (v.x / len, v.y / len, v.z / len);
}

template<typename T> tvec4<T> normalize(const tvec4<T> &v)
{
	T len = length(v);
	return tvec4<T> (v.x / len, v.y / len, v.z / len, v.w / len);
}

///	faceforward
template<typename T> tvec2<T> faceforward(const tvec2<T> &N,
										  const tvec2<T> &I, const tvec2<T> &Nref)
{
	if (dot(Nref, I) < 0)
		return N;
	return (-N);
}

template<typename T> tvec3<T> faceforward(const tvec3<T> &N,
										  const tvec3<T> &I, const tvec3<T> &Nref)
{
	if (dot(Nref, I) < 0)
		return N;
	return (-N);
}

template<typename T> tvec4<T> faceforward(const tvec4<T> &N,
										  const tvec4<T> &I, const tvec4<T> &Nref)
{
	if (dot(Nref, I) < 0)
		return N;
	return (-N);
}

/// reflect (N must be normalized)
template<typename T> tvec2<T> reflect(const tvec2<T> &I,
									  const tvec2<T> &N)
{
	return (I - 2 * dot(N, I) * N);
}

template<typename T> tvec3<T> reflect(const tvec3<T> &I,
									  const tvec3<T> &N)
{
	return (I - 2 * dot(N, I) * N);
}

template<typename T> tvec4<T> reflect(const tvec4<T> &I,
									  const tvec4<T> &N)
{
	return (I - 2 * dot(N, I) * N);
}

/// refract (N must be normalized)
template<typename T> tvec2<T> refract(const tvec2<T> &I,
									  const tvec2<T> &N, real32 eta)
{
	real32 k = 1 - eta * eta * (1.0 - dot(N, I) * dot(N, I));
	if (k < 0)
		return T(0);
	return (eta * I - (eta * dot(N, I) + std::sqrt(k)) * N);
}

template<typename T> tvec3<T> refract(const tvec3<T> &I,
									  const tvec3<T> &N, real32 eta)
{
	real32 k = 1 - eta * eta * (1.0 - dot(N, I) * dot(N, I));
	if (k < 0)
		return T(0);
	return (eta * I - (eta * dot(N, I) + std::sqrt(k)) * N);
}

template<typename T> tvec4<T> refract(const tvec4<T> &I,
									  const tvec4<T> &N, real32 eta)
{
	real32 k = 1 - eta * eta * (1.0 - dot(N, I) * dot(N, I));
	if (k < 0)
		return T(0);
	return (eta * I - (eta * dot(N, I) + std::sqrt(k)) * N);
}

///
/// relational
///

/// less than
template<typename T> bvec2 less_than(const tvec2<T> &v0,
									 const tvec2<T> &v1)
{
	return bvec2(v0.x < v1.x, v0.y < v1.y);
}

template<typename T> bvec3 less_than(const tvec3<T> &v0,
									 const tvec3<T> &v1)
{
	return bvec3(v0.x < v1.x, v0.y < v1.y, v0.z < v1.z);
}

template<typename T> bvec4 less_than(const tvec4<T> &v0,
									 const tvec4<T> &v1)
{
	return bvec4(v0.x < v1.x, v0.y < v1.y, v0.z < v1.z, v0.w < v1.w);
}

/// less than equal
template<typename T> bvec2 less_than_equal(const tvec2<T> &v0,
										   const tvec2<T> &v1)
{
	return bvec2(v0.x <= v1.x, v0.y <= v1.y);
}

template<typename T> bvec3 less_than_equal(const tvec3<T> &v0,
										   const tvec3<T> &v1)
{
	return bvec3(v0.x <= v1.x, v0.y <= v1.y, v0.z <= v1.z);
}

template<typename T> bvec4 less_than_equal(const tvec4<T> &v0,
										   const tvec4<T> &v1)
{
	return bvec4(v0.x <= v1.x, v0.y <= v1.y, v0.z <= v1.z, v0.w <= v1.w);
}

/// greater than
template<typename T> bvec2 greater_than(const tvec2<T> &v0,
										const tvec2<T> &v1)
{
	return bvec2(v0.x > v1.x, v0.y > v1.y);
}

template<typename T> bvec3 greater_than(const tvec3<T> &v0,
										const tvec3<T> &v1)
{
	return bvec3(v0.x > v1.x, v0.y > v1.y, v0.z > v1.z);
}

template<typename T> bvec4 greater_than(const tvec4<T> &v0,
										const tvec4<T> &v1)
{
	return bvec4(v0.x > v1.x, v0.y > v1.y, v0.z > v1.z, v0.w > v1.w);
}

/// greater than equal
template<typename T> bvec2 greater_than_equal(const tvec2<T> &v0,
											  const tvec2<T> &v1)
{
	return bvec2(v0.x >= v1.x, v0.y >= v1.y);
}

template<typename T> bvec3 greater_than_equal(const tvec3<T> &v0,
											  const tvec3<T> &v1)
{
	return bvec3(v0.x >= v1.x, v0.y >= v1.y, v0.z >= v1.z);
}

template<typename T> bvec4 greater_than_equal(const tvec4<T> &v0,
											  const tvec4<T> &v1)
{
	return bvec4(v0.x >= v1.x, v0.y >= v1.y, v0.z >= v1.z, v0.w >= v1.w);
}

/// equal
template<typename T> bvec2 equal(const tvec2<T> &v0, const tvec2<T> &v1)
{
	return bvec2(v0.x == v1.x, v0.y == v1.y);
}

template<typename T> bvec3 equal(const tvec3<T> &v0, const tvec3<T> &v1)
{
	return bvec3(v0.x == v1.x, v0.y == v1.y, v0.z == v1.z);
}

template<typename T> bvec4 equal(const tvec4<T> &v0, const tvec4<T> &v1)
{
	return bvec4(v0.x == v1.x, v0.y == v1.y, v0.z == v1.z, v0.w == v1.w);
}

/// not equal
template<typename T> bvec2 not_equal(const tvec2<T> &v0,
									 const tvec2<T> &v1)
{
	return bvec2(v0.x != v1.x, v0.y != v1.y);
}

template<typename T> bvec3 not_equal(const tvec3<T> &v0,
									 const tvec3<T> &v1)
{
	return bvec3(v0.x != v1.x, v0.y != v1.y, v0.z != v1.z);
}

template<typename T> bvec4 not_equal(const tvec4<T> &v0,
									 const tvec4<T> &v1)
{
	return bvec4(v0.x != v1.x, v0.y != v1.y, v0.z != v1.z, v0.w != v1.w);
}

/// any
inline bool any(const bvec2 &v)
{
	return (v.x || v.y);
}
inline bool any(const bvec3 &v)
{
	return (v.x || v.y || v.z);
}
inline bool any(const bvec4 &v)
{
	return (v.x || v.y || v.z || v.w);
}

/// all
inline bool all(const bvec2 &v)
{
	return (v.x && v.y);
}
inline bool all(const bvec3 &v)
{
	return (v.x && v.y && v.z);
}
inline bool all(const bvec4 &v)
{
	return (v.x && v.y && v.z && v.w);
}

// not
inline bvec2 notVec(const bvec2 &v)
{
	return bvec2(!v.x, !v.y);
}
inline bvec3 notVec(const bvec3 &v)
{
	return bvec3(!v.x, !v.y, !v.z);
}
inline bvec4 notVec(const bvec4 &v)
{
	return bvec4(!v.x, !v.y, !v.z, !v.w);
}

// interpolation
template<typename T> tvec2<T> mix(const tvec2<T> &v0,
								  const tvec2<T> &v1, T t)
{
	return tvec2<T> ((T(1) - t) * v0 + v1 * t);
}

template<typename T> tvec3<T> mix(const tvec3<T> &v0,
								  const tvec3<T> &v1, T t)
{
	return tvec3<T> ((T(1) - t) * v0 + v1 * t);
}

template<typename T> tvec4<T> mix(const tvec4<T> &v0,
								  const tvec4<T> &v1, T t)
{
	return tvec4<T> ((T(1) - t) * v0 + v1 * t);
}

///
/// @name min
/// @return the min between two value
/// @{
///
template<typename T> tvec2<T> min(const tvec2<T>& a, const tvec2<T> &b)
{
	return tvec2<T>(std::min(a.x, b.x), std::min(a.y, b.y));
}

template<typename T> tvec3<T> min(const tvec3<T>& a, const tvec3<T> &b)
{
	return tvec3<T>(std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z));
}

template<typename T> tvec4<T> min(const tvec4<T>& a, const tvec4<T> &b)
{
	return tvec4<T>(std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z), std::min(a.w, b.w));
}
/// @}

///
/// @name max
/// @return the max between two value
/// @{
///
template<typename T> tvec2<T> max(const tvec2<T>& a, const tvec2<T> &b)
{
	return tvec2<T>(std::max(a.x, b.x), std::max(a.y, b.y));
}

template<typename T> tvec3<T> max(const tvec3<T>& a, const tvec3<T> &b)
{
	return tvec3<T>(std::max(a.x, b.x), std::max(a.y, b.y), std::max(a.z, b.z));
}

template<typename T> tvec4<T> max(const tvec4<T>& a, const tvec4<T> &b)
{
	return tvec4<T>(std::max(a.x, b.x), std::max(a.y, b.y), std::max(a.z, b.z), std::max(a.w, b.w));
}
/// @}

///
/// @brief get the left orthogonal vector
/// @param v the input vector
/// @return the left orthogonal vector: tvec2<T>(-v.y, v.x)
///
template<typename T> tvec2<T> orthogonal(const tvec2<T>& v)
{
	return tvec2<T>(-v.y, v.x);
}

} // namespace math
} // namespace raja

#endif // RAJA_MATH_VECTOR
